package arg

import (
	"database/sql"
	"db2s/global"
	"encoding/json"
	"fmt"
	"github.com/gurkankaymak/hocon"
	"reflect"
	"strings"
	"time"
)

type rowsLevel struct {
	Options    string            `json:"options"`
	Delay      int               `json:"delay"`
	Rule       map[string]string `json:"rule"`
	EndTime    int               `json:"endTime"`
	IgColumn   bool              `json:"igColumn"`
	SqlMode    bool              `json:"sqlmode"`
	BreakPoint bool              `json:"breakpoint"`
	Scn        string            `json:"scn"`
}

func oracleMd5Func(db *sql.DB) (b bool) {
	createMd5FuncSql := `CREATE OR REPLACE FUNCTION MD5( passwd IN clob) RETURN VARCHAR2 IS
    	retval varchar2(32);
	BEGIN
    	retval := (case when passwd is null then null else utl_raw.cast_to_raw(DBMS_OBFUSCATION_TOOLKIT.MD5(INPUT_STRING => passwd)) end);
    	RETURN retval;
	END;
	`
	if _, err := db.Exec(createMd5FuncSql); err != nil {
		fmt.Println("create md5 func fail")
		return false
	}
	if _, err := db.Exec("commit"); err != nil {
		fmt.Println("commit md5 func fail")
		return false
	}
	return true
}

func oracleMd5FuncCheck(db *sql.DB) (b bool) {
	var (
		rows *sql.Rows
		err  error
	)
	defer func() {
		if rows != nil {
			if err = rows.Close(); err != nil {
				fmt.Println(fmt.Sprintf("db2s report: oracleMd5FuncCheck close rows.close fail. error info is %s", err))
			}
		}
	}()
	if rows, err = db.Query("select MD5('1') as \"md5Sum\" from dual"); err != nil {
		fmt.Println("db2s report: oracle md5 function not exist,err info is ", err)
		return false
	}
	return true
}

// rowsRuleSecondaryLevelParameterCheck
func rowsRuleParameter(hocon *hocon.Config) (rows RowsRules, err error) {
	//rc.SecondaryL.RulesV.CheckMode = rc.getObjectName(checkMod)
	//rc.SecondaryL.FunctionalMode = rc.getObjectName(checkMod)
	var (
		s1      []string
		rowsMap map[string]any
		//f3      = rc.FirstL1.Rules.ToConfig().GetObject(rc.getObjectName(checkMod)).ToConfig()
	)
	if rowsMap, err = global.StructToMapAny(getRowsRules()); err != nil {
		return
	}
	for k, v := range rowsMap {
		if r1 := rc.getHoconConfigVal(hocon, k, reflect.TypeOf(v).String()); r1 != nil {
			s1 = append(s1, rc.anyToJsonString(k, r1))
		} else {
			s1 = append(s1, rc.anyToJsonString(k, v))
		}
	}
	var s2 string
	if strings.EqualFold(s2, "{}") {
		s2 = fmt.Sprintf("\"%s\":{%s}", "rows", strings.Join(s1, ","))
	} else {
		s2 = fmt.Sprintf("{%s}", strings.Join(s1, ","))
	}
	if err = json.Unmarshal([]byte(strings.ReplaceAll(s2, "\\", "\\\\")), &rows); err != nil {
		return
	}
	rows.OutTime = rows.OutTime * time.Second
	return
}

// rowsParameterCheck
func rowsParameterCheck(s *SecondaryLevel) error {
	var (
		checkMethodOptionalValue = map[string]int{"crc32": 1, "md5": 1, "data": 1}
	)
	if _, ok := checkMethodOptionalValue[s.RulesV.RowsR.Options]; !ok {
		s.RulesV.RowsR.Options = "crc32"
	}
	if strings.EqualFold(s.DSnsV.SrcDBName, "oracle") || strings.EqualFold(s.DSnsV.DestDBName, "oracle") {
		if strings.EqualFold(s.RulesV.RowsR.Options, "crc32") {
			s.RulesV.RowsR.Options = "md5"
		}
		//测试oracle是否存在md5函数
		if s.RulesV.RowsR.Options == "md5" {
			var tmpT = false
			for k, v := range map[string]*sql.DB{s.DSnsV.SrcDBName: s.DSnsV.CheckParameterSourceDB,
				s.DSnsV.DestDBName: s.DSnsV.CheckParameterDestDB} {
				if strings.EqualFold(k, "oracle") {
					if !oracleMd5FuncCheck(v) {
						if !oracleMd5Func(v) && !oracleMd5FuncCheck(v) {
							tmpT = true
						}
					}
				}
			}
			if tmpT {
				s.RulesV.RowsR.Options = "data"
			}
		}
	}
	if s.RulesV.RowsR.DelayCheck < 0 {
		s.RulesV.RowsR.DelayCheck = 0
	}
	return nil
}
